#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the version 3 of the GNU Lesser General Public License
#   as published by the Free Software Foundation.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright (c) NEC Deutschland GmbH, NEC HPC Europe
#
# $Id$


import os

from aggmon.databases.metric.time_series_database import TimeSeriesDatabase, NS_IN_S
from aggmon.databases.metric.rra import RRA
from basic_types import NumRecord
from aggmon.databases.metric.database_daemon import DatabaseDaemon


__all__ = ["RRD"]


class RRD(TimeSeriesDatabase):
    """
    A RRD database contains the history of numeric-typed metrics.
    It is only guaranteed that successive values are increasing in time. But steps may vary.
    All data is sliced in files, where each file contains data of one rotation_period.
    If more than keep_exact + 1 files are filled data gets condensed in an RRA object.
    """

    def append( self, time_ns, value ):
        """
        Append a value to the current RRD database.
        @param time_ns: time in 10E-9 seconds since epoch
        @param value: the value to be added to the record.
        @return: nothing
        """
        self.append_record( NumRecord( time_ns, value ) )
        #check if there are files to condense
        if self.number_of_files_to_condense() > 0:
            int_file_list = self.get_files_to_condense()
            DatabaseDaemon.createDaemon( int_file_list )

    def cascade( self, int_file_list ):
        """
        Condense files.
        """
        import logging
        file_path = os.path.dirname( int_file_list[0] )
        for file_name in int_file_list:
            rra = RRA( file_path, keep_exact=self._keep_exact )
            rotation_period_rrd = self.get_rotation_period()
            rotation_period_rra = rra.get_rotation_period()
            step_ns = rotation_period_rrd / int( rotation_period_rrd / int( rotation_period_rra / rra.getWidth() ) ) * NS_IN_S
            records = super( RRD, self ).fetch_all( selected_files=[file_name] )
            logging.debug( "RRD.cascade(): %d records read from %s" % (len( records ), file_name) )
            #self._dump_records( records )
            records = super( RRD, self ).fetch_all( step_ns=step_ns , selected_files=[file_name] )
            logging.debug( "RRD.cascade(): num of condensed records %d" % len( records ) )
            #self._dump_records( records )
            for record in records:
                rra.insert( record.time_ns, record.value )
            # remove RRD file
            os.unlink( file_name )
            logging.debug( "RRD.cascade(): removed file " + file_name )

        if rra.number_of_files_to_condense() > 0:
            int_file_list = rra.get_files_to_condense()
            rra.cascade( int_file_list )

    def fetch_all( self, from_ns=0, to_ns=0, nsteps=0, step_ns=0, selected_files=None ):
        """
        This function overrides fetch_all in TimeSeriesDatabase and digs into
        condensed data files. If a file to read from is specified just read it and leave.
        """
        records = []
        if selected_files == None:
            rra = RRA( self._dir_path, keep_exact=self._keep_exact )
            records = rra.fetch_all( from_ns, to_ns, nsteps, step_ns )
        records.extend( super( RRD, self ).fetch_all( from_ns, to_ns, nsteps, step_ns, selected_files ) )
        return records

    def insert( self, time_ns, value ):
        """
        Inserts a time, value pair into the database.
        @param time_ns: time in 10E-9 seconds since epoch
        @param value: the actual value of the metric
        @return: nothing
        """
        self.insert_record( NumRecord( time_ns, value ) )

    def record( self ):
        return NumRecord()
    

from basic_types.factory import Factory
Factory.set_factory( "RRD", RRD, __name__ )
